package com.fm.jbsql.sql;

import com.fm.jbsql.util.CmdOpt;
import com.fm.jbsql.util.JbsqlException;

import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.*;

/**
 * Created by IntelliJ IDEA.
 * User: syaroshenko
 * Date: Jul 13, 2006
 * Time: 11:36:27 AM
 * Class to compile SQL-objects and visualize results
 */

/**
 * Class to compile SQL-objects and visualize results.
 */
public class SQLScriptCompiler {
    private final Logger logger = Logger.getLogger("com.fm.jbsql.sql.SQLScriptCompiler");

    /**
     * Compile SQL-object.
     * @param cmdopt command line arguments
     * @param drv JDBC driver
     * @param con Connection
     * @param fileName file name to compile
     * @throws NullPointerException
     * @throws JbsqlException
     */
    public final void compile(final CmdOpt cmdopt, final DbDriver drv, final Connection con, final String fileName) throws NullPointerException, JbsqlException {
        logger.log(Level.FINE, "start");
        if ( con == null ) throw new NullPointerException();
        if ( fileName == null ) throw new NullPointerException();

        BufferedReader in;

        try { in = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "utf8".equals(cmdopt.getClientCharset()) ? "UTF8" : "ISO-8859-1")); }
        catch (FileNotFoundException ex) { throw new JbsqlException("FNF_01", fileName, ex); }
        catch (UnsupportedEncodingException ex) { throw new JbsqlException("UEE_01", fileName, ex); }

        try {
            con.clearWarnings();
            boolean eof = false;
            boolean isCodeBefore = false, isCodeAfter = false;
            String queryBefore = "";
            String queryAfter = "";
            int type;
            String line;
            StringBuffer query = new StringBuffer();
            while ( !eof ) {
                query.setLength(0);
                while ( true ) {
                    try { line = in.readLine(); }
                    catch (IOException ex) { throw new JbsqlException("IOE_04", fileName, ex); }
                    if ( line == null ) {
                        eof = true;
                        break;
                    }

                    if ( (type=drv.getReservedToken(line.trim())) == DbDriver.ETokenType.ETT_SAVETERMINATOR ) {
                        query.append(line);
                        break;
                    }
                    if ( type == DbDriver.ETokenType.ETT_TERMINATOR ) break;

                    query.append(line);
                    query.append("\n");
                    if ( type == DbDriver.ETokenType.ETT_CODE_BEFORE_START ) {
                        // verify if "CODE AFTER" tag was opened
                        if ( isCodeAfter ) {
                            if ( queryAfter.trim().length() != 0 ) _execSQL(con, drv, queryAfter, cmdopt);
                            isCodeAfter = false;
                        }
                        isCodeBefore = true;
                        continue;
                    }
                    if ( type == DbDriver.ETokenType.ETT_CODE_AFTER_START ) {
                        // verify if "CODE BEFORE" tag was opened
                        if ( isCodeBefore ) {
                            if ( queryBefore.trim().length() != 0 ) _execSQL(con, drv, queryBefore, cmdopt);
                            isCodeBefore = false;
                        }
                        isCodeAfter = true;
                        continue;
                    }
                    if ( isCodeBefore || isCodeAfter ) {
                        if ( type == DbDriver.ETokenType.ETT_CODE_END ) {
                            // verify absent CODE AFTER
                            if ( isCodeBefore ) {
                                if ( queryBefore.trim().length() != 0 ) _execSQL(con, drv, queryBefore, cmdopt);
                                isCodeBefore = false;
                            }
                            isCodeAfter = false;
                            continue;
                        }

                        if ( isCodeBefore ) queryBefore += line + "\n";
                        else if ( isCodeAfter ) queryAfter += line + "\n";
                    }
                }
                if (query.toString().trim().length() == 0) continue;
                _execSQL(con, drv, query.toString(), cmdopt);
            }
            if ( queryAfter.trim().length() != 0 ) _execSQL(con, drv, queryAfter, cmdopt);
        }
        catch (SQLException ex) {
            throw new JbsqlException("SQL_01", null, ex);
        }
        finally {
            if ( in != null ) {
                try { in.close(); }
                catch (IOException ex) { ex.printStackTrace(); }
            }
        }
    }
	
    private void _execSQL(final Connection con, DbDriver drv, final String query, final CmdOpt cmdopt) throws JbsqlException {
        logger.info("executing ["+ query +"]");
        try{
        	this.execSQL( con, drv, query, cmdopt);
        }catch(SQLException ex){
        	throw new JbsqlException("SQL_01", null, ex);
        }
    	
    }
    
    protected void execSQL(final Connection con, DbDriver drv, final String query, final CmdOpt cmdopt) throws SQLException,JbsqlException {
        Statement stmt = null;
        boolean results = false;
        ResultSet rs = null;
        try {
            stmt = con.createStatement();
            stmt.setEscapeProcessing(false);
           	rs = stmt.executeQuery(query);
        } catch (SQLException ex) {
            if ( drv.checkForWarning(ex) ) {
               	if ( stmt != null ) stmt.cancel();
                throw ex;
            }
        }
        drv.checkForWarning(stmt);
        if ( rs != null ) {
            do {
                try {
                    rs = stmt.getResultSet();
                    DbDriver.displayResultSet(rs, cmdopt);
		            drv.checkForWarning(stmt);
                    rs.close();
                    results = stmt.getMoreResults();
                } catch (SQLException ex) {
                    if ( drv.checkForWarning(ex) ) throw ex;
                }
            } while (results);
        }
    }
}

